home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-06-30 | 16.0 KB | 465 lines | [TEXT/PJMM] |
-
- { The following program sample demonstrates how one can use the Ethernet routines }
- { described in Inside Macintosh volume VI. To simplify this sample, the program}
- { does not include the traditional Macintosh Toolbox initialization code. The program }
- { instead demonstrates the basic use of the various Control calls to the ENET driver. }
- {}
- { To run this program, you'll need two Mac connected to Ethernet. Have Macsbug installed}
- { on both systems, as it is the user interface to demonstrate that the program has entered}
- { the completion routine when the ERead call completes. Launch a copy of this }
- { on the first system, then launch a second copy on the other system. When the second }
- { system sends the packet to the first, the completion routine gets called and you drop into}
- { Macsbug momentarily. Assuming that things go well, that's the only time you'll enter}
- { macsbug. To get out of the program, click the mouse button. }
- {}
- { Written By: Rich Kubota }
- { Date 6/93 }
- {}
-
-
- PROGRAM ENETTest;
-
- {$IFC THINK_Pascal}
- USES
- ROMDefs, Slots, AppleTalk, ENET;
- {$ELSEC}
- USES
- Types, QuickDraw, Errors, Memory, Devices, ROMDefs, OSUtils, Slots, AppleTalk, ENET;
- {$ENDC}
-
- CONST
- kMyProtocol = $9090; { must be > $5DC or you'll replace the }
- { default handler which would be a bad thing}
-
- TYPE { define new EParamBlock type to append }
- MyEParamBlock = RECORD { an A5 value to the end of the record }
- pb: EParamBlock; { This mod is for the demo completion routine }
- myA5: LONGINT;
- END;
-
- MyEParamBlkPtr = ^MyEParamBlock;
-
- VAR { global param block used to track calls to ERead }
- gMyEPB: MyEParamBlock;
- gMultiCastAddr: PACKED ARRAY[1..6] OF byte;
- gDone: Boolean;
-
- PROCEDURE DoError (err: INTEGER);
- BEGIN
- Debugger; { not the best interface, but I wanted this to be a }
- { a budget example }
- END;
-
- PROCEDURE ProcessData (aptr: Ptr);
- BEGIN
- DebugStr('Processing data;g'); { again, not the best user interface, }
- { but there's not a whole lot you can }
- { do at interrupt time }
- END;
-
- PROCEDURE Detach_SamplePH (ENETRefNum: INTEGER);
- FORWARD;
-
- {*****************************************************************************}
- { The following initializes the global variable gMultiCastAddr so that we }
- { only set it's value here and not have to worry about it's value throughout }
- { the remainder of this program. In addition we can then use BlockMove to }
- { fill in the various paramblock fields and not have to worry about byte sized}
- { values being filled into word sized locations. }
- {*****************************************************************************}
-
- PROCEDURE Init_Multicast_Address;
- BEGIN
- gMultiCastAddr[1] := $09;
- gMultiCastAddr[2] := $00;
- gMultiCastAddr[3] := $2B;
- gMultiCastAddr[4] := $00;
- gMultiCastAddr[5] := $00;
- gMultiCastAddr[6] := $04;
- END;
-
-
- {*****************************************************************************}
- { The following function demonstrates how to identify and to open an Ethernet }
- { driver using the Slot Manager and the OpenSlot function. }
- { This function returns the driver refNum of the opened ENET driver or 0 if }
- { an error occured or an Ethernet card could not be identified }
- {*****************************************************************************}
-
- FUNCTION Get_And_Open_ENET_Driver: INTEGER;
-
- VAR
- mySBlk: SpBlock;
- myPBRec: ParamBlockRec;
- myErr: OSErr;
- Found: INTEGER;
- ENETRefNum: INTEGER;
- EnetStr: Str15;
- Enet0Str: Str15;
-
- BEGIN
- Found := 0; { assume no sResource found }
- ENETRefNum := 0; { indicate no driver found }
-
- WITH mySBlk DO { set up the spBlock }
- BEGIN
- spParamData := 1; { include search of disabled resources. }
- { Start searching item from spSlot and }
- { the slots above it as well. }
- spCategory := catNetwork;
- spCType := typeEthernet;
- spDrvrSW := 0;
- spDrvrHW := 0;
- spTBMask := 3; { match only Category and CType fields }
- spSlot := 0; { start search from here }
- spID := 0; { start search from here }
- spExtDev := 0; { ID of the external device }
- END;
-
- {REPEAT}
- { we could implement a repeat loop to check }
- { for multiple ENET cards, however in this sample }
- { we only grab the first one }
- myErr := SGetTypeSRsrc(@mySBlk);
- IF myErr = noErr THEN { you have found an sResource match }
- { save it for later }
- BEGIN
- Found := Found + 1;
- { SaveSInfo(@mySBlk); { save slot info for later use }
- END;
- {until myErr = smNoMoresRsrcs;}
-
- IF Found > 1 THEN
- BEGIN
- { If you found more than one sResource, put up a dialog box }
- { and let the user choose one. If any of the sResources you }
- { found were disabled, let the user know they are not available. }
- { For this example we assume that the selected slot is returned }
- { in mySBlk.spSlot along with the corresponding sResource ID }
- { in mySBlk.spID - and that Found remains > 1 to indicate to }
- { go ahead and open the driver. }
- END;
-
- IF found <> 0 THEN
- BEGIN
- EnetStr := '.ENET';
- WITH myPBRec DO
- BEGIN
- ioCompletion := NIL; { call made synchronously }
- ioNamePtr := @EnetStr;
- ioPermssn := fsCurPerm;
- ioFlags := 0; { reserved for driver use }
- ioSlot := mySBlk.spSlot; { slot of ENET card to open }
- ioID := mySBlk.spID { sResource ID for slot }
- END;
- myErr := OpenSlot(@myPBRec, FALSE); { go open slot }
- IF myErr = noErr THEN
- ENETRefNum := myPBRec.ioRefNum;
- END
- ELSE
- BEGIN
- Enet0Str := '.ENET0';
- myErr := OpenDriver(Enet0Str, ENETRefNum);
- END;
- IF myErr <> noErr THEN
- DoError(myErr); { go handle the error }
-
- Get_And_Open_ENET_Driver := ENETRefNum; { return the refNum or 0 if }
- { unsuccessful }
- END;
-
- {*****************************************************************************}
- { The following function demonstrates how to add a multicast address }
- { The function returns an result of type OSErr }
- {*****************************************************************************}
- FUNCTION Add_Multicast_Address (ENETRefNum: Integer): OSErr;
- VAR
- myPB: EParamBlock;
- myErr: OSErr;
-
- BEGIN
- { fill in the multicast address field using BlockMove from the }
- { global multicast variable - C programmers want to ensure that the }
- { eMultiAddr field is correctly defined in the ENET.h file }
- { This field immediately follows the csCode field in the EParamBlock }
- { structure }
- BlockMove(@gMultiCastAddr, @myPB.eMultiAddr, sizeof(gMultiCastAddr));
- myPB.ioRefNum := ENETRefNum;
- myErr := EAddMulti(@myPB, FALSE);
- IF myErr <> noErr THEN
- DoError(myErr);
-
- Add_Multicast_Address := myErr;
- END;
-
-
- {*****************************************************************************}
- { The following procedure is a sample completion routine. Note that the routine }
- { reuses the parameter block to issue a new ERead call. It's important to }
- { remember that when the completion routine is called, the A5 world will likely}
- { be different from that of the application and must be set up correctly before}
- { accessing any globals. In setting up the A5 world, save the original value so}
- { that it can be restored upon exit }
- {****************************************************************************}
- PROCEDURE MyCompRoutine (myEPBPtr: MyEParamBlkPtr);
-
- VAR
- myErr: OSErr;
- saveA5: LONGINT;
- aptr: Ptr;
-
- BEGIN
- DebugStr('Entering completion routine;g');
- IF (myEPBPtr^.pb.ioResult < noErr) THEN { was ERead successful }
- BEGIN
- IF (myEPBPtr^.pb.ioResult <> reqAborted) THEN
- { was request aborted? }
- DoError(myEPBPtr^.pb.ioResult) { something bad happened }
- END
- ELSE
- BEGIN { process the packet }
- saveA5 := SetA5(myEPBPtr^.myA5); { Set A5 to our world }
- aptr := myEPBPtr^.pb.EPointer;
- ProcessData(aptr); { do something with the data }
- saveA5 := SetA5(saveA5); { restore the A5 World }
- END;
-
-
- IF NOT gDone THEN { check whether we have been called because }
- { we're about to quit }
- BEGIN { if not, then make another ERead call }
- myErr := ERead(EParamBlkPtr(myEPBPtr), TRUE);
- { call ERead to wait for the next packet }
- IF myErr <> noErr THEN { check if error occured queueing ERead call }
- DoError(myErr);
- END;
- END; { of MyCompletion routine }
-
-
- {*****************************************************************************}
- { The following inline function demonstrates how get the parameter block }
- { pointer in register A0. This is needed by the completion routine to set }
- { up the A5 world correctly }
- {****************************************************************************}
- FUNCTION GetParamBlockPtr: Ptr; { Get the pointer to the parameter block out of }
- { A0 and place it on the stack in the location }
- { reserved for it }
- INLINE
- $2E88; { MOVE.L A0,(SP) }
-
- {*****************************************************************************}
- { The following procedure is a stub completion routine which is designed to }
- { get the parameter block pointer in A0 and call the real completion routine. }
- { It is possible that prior to calling the inline function, A0 could be used }
- { by the compiler for some other purpose. To minimize the chances of this }
- { happening, we do as little as possible in this procedure - grab register A0 }
- { then call another procedure where the real work will be performed. }
- {****************************************************************************}
- PROCEDURE MyStubCompRoutine;
-
- VAR
- myEPBPtr: MyEParamBlkPtr;
-
- BEGIN
- myEPBPtr := MyEParamBlkPtr(GetParamBlockPtr); { Get the PB ptr from A0 }
- myCompRoutine(myEPBPtr); { call real comp routine }
- END; { of MyCompletion routine }
-
-
- {*****************************************************************************}
- { The following function demonstrates how to call EAttachPH to use the }
- { default handler, then to issue the ERead call to handle any incoming }
- { packets. This routine returns a result of type OSErr to indicate whether }
- { an error occured during the process }
- {****************************************************************************}
- FUNCTION Sample_AttachPH_And_Read_Packet (ENETRefNum: INTEGER): OSErr;
-
- CONST
- kBigBytes = 8888;
-
- VAR
- myEPBPtr: MyEParamBlkPtr;
- aptr: Ptr;
- myErr: OSErr;
-
- BEGIN {Sample_AttachPH_And_Read_Packet }
- myEPBPtr := @gMyEPB; { set up EAttachPH parameters }
- WITH gMyEPB.pb DO
- BEGIN
- eProtType := kMyProtocol; { protocol type }
- ePointer := NIL; { indicate use of default handler }
- ioRefNum := ENETRefNum; { ENET driver refNum }
- END;
- myErr := EAttachPH(EParamBlkPtr(myEPBPtr), FALSE); { tell ENET about new }
- { to handle }
-
- IF myErr <> noErr THEN { check if error occured attaching protocol }
- DoError(myErr)
- ELSE
- BEGIN
- aptr := NewPtr(kBigBytes); { need to check the result of this memory }
- gMyEPB.myA5 := SetCurrentA5; { store the current a5 world }
- WITH gMyEPB.pb DO { allocation }
- BEGIN
- ioCompletion := @MyStubCompRoutine; { pointer to completion routine }
- eProtType := kMyProtocol; { protocol type to respond to }
- ePointer := aptr; { pointer to read-data area }
- eBuffSize := kBigBytes; { size of read - data area }
- ioRefNum := ENETRefNum; { set ENET driver refNum }
- END;
- myErr := ERead(EParamBlkPtr(myEPBPtr), TRUE);
-
- IF myErr <> noErr THEN { check if error occured queueing read request }
- BEGIN
- DoError(myErr); { process error result }
- Detach_SamplePH(ENETRefNum); { detach the protocol handler }
- END;
- END;
- Sample_AttachPH_And_Read_Packet := myErr; { return the error result }
- END; {Sample_AttachPH_And_Read_Packet }
-
- {*****************************************************************************}
- { The following function demonstrates how to send a sample Ethernet packet }
- { For this sample, a multicast address is used instead of a local hardware }
- { address. The function returns an result of type OSErr }
- {*****************************************************************************}
- FUNCTION Send_Sample_ENET_Packet (ENETRefNum: Integer): OSErr;
- CONST
- kSIZE1 = 100;
- kSIZE2 = 333;
-
- TYPE
- WDS = RECORD { write-data structure }
- length: INTEGER; { length of nth entry }
- aptr: Ptr; { pointer to nth entry }
- END;
-
- VAR
- myWDS: ARRAY[1..4] OF WDS;
- myPB: EParamBlock; { .ENET parameter block }
- wheader: PACKED ARRAY[0..13] OF Byte;
- stuff1: PACKED ARRAY[1..kSIZE1] OF Byte;
- stuff2: PACKED ARRAY[1..kSIZE2] OF Byte;
- myErr: OSErr;
-
- BEGIN
- { set up the write header }
- { use the multicast address as the destination address and }
- { copy to the first 6 bytes of the wheader array }
- BlockMove(@gMultiCastAddr, @wheader, sizeof(gMultiCastAddr));
-
- { bytes 7 -12 are reserved for the }
- { source ENET address }
-
- wheader[12] := $90; { protocol type }
- wheader[13] := $90; { this must match kProtocol value }
-
- myWDS[1].length := 14;
- myWDS[1].aptr := @wheader;
- myWDS[2].length := kSIZE1;
- myWDS[2].aptr := @stuff1;
- myWDS[3].length := kSIZE2;
- myWDS[3].aptr := @stuff2;
- myWDS[4].length := 0;
-
- myPB.ePointer := @myWDS;
- myPB.ioRefNum := ENETRefNum;
-
- myErr := EWrite(@myPB, FALSE); { send something }
- IF myErr <> noErr THEN
- DoError(myErr);
- Send_Sample_ENET_Packet := myErr;
- END;
-
-
- {*****************************************************************************}
- { The following procedure demonstrates how to call EDetachPH to remove the }
- { specified protocol from the list of protocols on which the ENET driver should }
- { respond to }
- {****************************************************************************}
- PROCEDURE Detach_SamplePH (ENETRefNum: INTEGER);
- VAR
- myPB: EParamBlock;
- myErr: OSErr;
- BEGIN
- myPB.eProtType := kMyProtocol;
- myPB.ioRefNum := ENETRefNum;
- myErr := EDetachPH(@myPB, FALSE);
- END;
-
-
- {*****************************************************************************}
- { The following procedure demonstrates how to call EDelMulti to remove the }
- { a multicast address from the list of addresses that the driver should accept }
- { packets for }
- {****************************************************************************}
- PROCEDURE Delete_Multicast_Address (ENETRefNum: INTEGER);
- VAR
- myPB: EParamBlock;
- myErr: OSErr;
- BEGIN
- { see note above in Add_Multicast_Address function code }
- BlockMove(@gMultiCastAddr, @myPB.eMultiAddr, sizeof(gMultiCastAddr));
- myPB.ioRefNum := ENETRefNum;
- myErr := EDelMulti(@myPB, FALSE);
- IF myErr <> noErr THEN
- DoError(myErr);
- END;
-
-
- {*****************************************************************************}
- { The following procedure demonstrates how to call ERdCancel to remove a }
- { queued read request to the ENET driver }
- {****************************************************************************}
- PROCEDURE Call_ERdCancel;
- VAR
- myPB: EParamBlock;
- myErr: OSErr;
- BEGIN
- myPB.ePointer := Ptr(@gMyEPB);
- myPB.ioRefNum := gMyEPB.pb.ioRefNum;
- myErr := ERdCancel(@myPB, FALSE);
- END;
-
-
-
- {*****************************************************************************}
- { Here's the MAIN program where we put everything together }
- {****************************************************************************}
-
- VAR
- myErr: OSErr;
- ENETRefNum: INTEGER;
-
- BEGIN
- gDone := FALSE; { initialize gDone }
- Init_Multicast_Address;
- ENETRefNum := Get_And_Open_ENET_Driver;
- IF ENETRefNum <> 0 THEN
- BEGIN
- myErr := Add_Multicast_Address(ENETRefNum);
- IF myErr = noErr THEN
- BEGIN
- myErr := Sample_AttachPH_And_Read_Packet(ENETRefNum);
-
- IF myErr = noErr THEN
- BEGIN
-
- myErr := Send_Sample_ENET_Packet(ENETRefNum);
- IF myErr <> noErr THEN
- DoError(myErr);
-
- { do application defined tasks here }
-
- REPEAT
- UNTIL Button;
- END;
- gDone := TRUE; { indicate that we are getting ready to quit }
- IF gMyEPB.pb.ioResult = 1 THEN
- Call_ERdCancel;
- Detach_SamplePH(ENETRefNum);
-
- END;
- Delete_Multicast_Address(ENETRefNum);
- END;
- END.